﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;

namespace Xwltz.Framework.Helper
{
    public class PayJsHelper
    {
        private readonly Dictionary<string, string> _apiList = new Dictionary<string, string>();

        public PayJsHelper()
        {
            _apiList.Add("NativePay", "native");
            _apiList.Add("CashierPay", "cashier");
            _apiList.Add("JsApiPay", "jsapi");
            _apiList.Add("MicroPay", "micropay");
            _apiList.Add("FacePay", "facepay");
            _apiList.Add("CheckOrder", "check");
            _apiList.Add("CloseOrder", "close");
            _apiList.Add("RefundOrder", "refund"); 
            _apiList.Add("UserInfo", "user");
            _apiList.Add("MchInfo", "info");
            _apiList.Add("ReverseOrder", "reverse"); // 未实现
            _apiList.Add("PayTo", "payto"); // 未实现
            _apiList.Add("OpenId", "openid"); // 未实现
            _apiList.Add("Bank", "bank"); // 未实现
        }

        private readonly string _mchid;

        private readonly string _key;

        private readonly Dictionary<string, string> _apiUrl;

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="mchid"></param>
        /// <param name="key"></param>
        /// <param name="server"></param>
        public PayJsHelper(string mchid, string key, string server = "https://payjs.cn/api/")
        {
            //Payjs pay = new Payjs("YOUR MCHID", "YOUR KEY");
            _mchid = mchid;
            _key = key;

            _apiUrl = new Dictionary<string, string>();
            foreach (var apiName in _apiList)
            {
                _apiUrl.Add(apiName.Key, server + apiName.Value);
            }
        }

        /// <summary>
        /// 收款码
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public string NativePay(Dictionary<string, string> param)
        {
            /**
             * Dictionary<string, string> param = new Dictionary<string, string>();
             * param["total_fee"] = "1";
             * param["out_trade_no"] = DateTime.Now.Ticks.ToString();
             * param["body"] = "test";
             * param["attch"] = "user id";
             * param["notify_url"] = "YOUR notify_url";
             * //返回原始json字符串
             * string jsonString = pay.NativePay(param);
             */
            var url = _apiUrl[MethodBase.GetCurrentMethod().Name];
            return Post(url, param);
        }

        /// <summary>
        /// 收银台
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public string CashierPay(Dictionary<string, string> param)
        {
            /**
             * Dictionary<string, string> param = new Dictionary<string, string>();
             * param["total_fee"] = "1";
             * param["out_trade_no"] = DateTime.Now.Ticks.ToString();
             * param["body"] = "test";
             * param["attch"] = "user id";
             * param["notify_url"] = "YOUR notify_url";
             * param["callback_url"] = "YOUR callback_url";
             * //返回原始json字符串
             * string url = pay.CashierPay(param);
             */
            var url = _apiUrl[MethodBase.GetCurrentMethod().Name];
            return url + "?" + BuildParam(param);
        }

        /// <summary>
        /// JSAPI
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public string JsApiPay(Dictionary<string, string> param)
        {
            /**
             * Dictionary<string, string> param = new Dictionary<string, string>();
             * param["total_fee"] = "1";
             * param["out_trade_no"] = DateTime.Now.Ticks.ToString();
             * param["body"] = "test";
             * param["attch"] = "user id";
             * param["notify_url"] = "YOUR notify_url";
             * param["openid"] = "user's openid";//获取用户openid：https://help.payjs.cn/api-lie-biao/huo-qu-openid.html
             * //返回原始json字符串
             * string jsonString = pay.JsApiPay(param);
             */
            string url = _apiUrl[MethodBase.GetCurrentMethod().Name];
            return Post(url, param);
        }

        /// <summary>
        /// 刷卡支付
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public string MicroPay(Dictionary<string, string> param)
        {
            /**
             * Dictionary<string, string> param = new Dictionary<string, string>();
             * param["total_fee"] = "1";
             * param["out_trade_no"] = DateTime.Now.Ticks.ToString();
             * param["body"] = "test";
             * param["attch"] = "user id";
             * param["auth_code"] = "YOUR auth_code";
             * //返回原始json字符串
             * string jsonString = pay.MicroPay(param);
             */
            string url = _apiUrl[MethodBase.GetCurrentMethod().Name];
            return Post(url, param);
        }

        /// <summary>
        /// 人脸支付
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public string FacePay(Dictionary<string, string> param)
        {
            /**
             * Dictionary<string, string> param = new Dictionary<string, string>();
             * param["total_fee"] = "1";
             * param["out_trade_no"] = DateTime.Now.Ticks.ToString();
             * param["body"] = "test";
             * param["attch"] = "user id";
             * param["face_code"] = "YOUR face_code";
             * param["openid"] = "user's openid";
             * //返回原始json字符串
             * string jsonString = pay.FacePay(param);
             */
            string url = _apiUrl[MethodBase.GetCurrentMethod().Name];
            return Post(url, param);
        }

        /// <summary>
        /// 订单查询
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public string CheckOrder(Dictionary<string, string> param)
        {
            /**
             * Dictionary<string, string> param = new Dictionary<string, string>();
             * param["payjs_order_id"] = "2018xxxxxxx";
             * //返回原始json字符串
             * string jsonString = pay.CheckOrder(param);
             */
            string url = _apiUrl[MethodBase.GetCurrentMethod().Name];
            return Post(url, param);
        }

        /// <summary>
        /// 关闭订单
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public string CloseOrder(Dictionary<string, string> param)
        {
            /**
             * Dictionary<string, string> param = new Dictionary<string, string>();
             * param["payjs_order_id"] = "2018xxxxxxx";
             * //返回原始json字符串
             * string jsonString = pay.CloseOrder(param);
             */
            string url = _apiUrl[MethodBase.GetCurrentMethod().Name];
            return Post(url, param);
        }

        /// <summary>
        /// 退款
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public string RefundOrder(Dictionary<string, string> param)
        {
            /**
             * Dictionary<string, string> param = new Dictionary<string, string>();
             * param["payjs_order_id"] = "2018xxxxxxx";
             * //返回原始json字符串
             * string jsonString = pay.RefundOrder(param);
             */
            string url = _apiUrl[MethodBase.GetCurrentMethod().Name];
            return Post(url, param);
        }

        /// <summary>
        /// 异步通知的数据校验
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public bool NotifyCheck(Dictionary<string, string> param)
        {
            //param参数为PAYJS服务器POST过来的数据，根据自己项目中所用的框架获取POST数据后，转为Dictionary<string, string> 类型传入NotifyCheck方法
            //返回是否验证成功
            //bool issuccess = pay.NotifyCheck(param);

            string originSign = param["sign"];
            param.Remove("sign");
            return Sign(param)["sign"] == originSign;
        }

        /// <summary>
        /// 用户详情
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public string UserInfo(Dictionary<string, string> param)
        {
            /**
             * Dictionary<string, string> param = new Dictionary<string, string>();
             * param["openid"] = "2018xxxxxxx";
             * //返回原始json字符串
             * string jsonString = pay.UserInfo(param);
             */
            string url = _apiUrl[MethodBase.GetCurrentMethod().Name];
            return Post(url, param);
        }

        /// <summary>
        /// 商户资料
        /// </summary>
        /// <returns></returns>
        public string MchInfo()
        {
            /**
             * //返回原始json字符串
             * string jsonString = pay.MchInfo(new Dictionary<string, string>());
             */

            string url = _apiUrl[MethodBase.GetCurrentMethod().Name];
            return Post(url, new Dictionary<string, string>());
        }

        private Dictionary<string, string> Sign(IDictionary<string, string> param)
        {
            param.Add("mchid", _mchid);
            //去掉空的，排序
            var newParam = param.Where(w => w.Value.Trim() != "").OrderBy(o => o.Key).ToDictionary(d => d.Key, d => d.Value);
            var paramStr = "";
            //拼接
            foreach (var keyPair in newParam)
            {
                paramStr += (keyPair.Key + "=" + keyPair.Value + "&");
            }
            //加上key
            paramStr += "key=" + _key;
            //md5后大写
            var sign = (Md5(paramStr)).ToUpper();

            newParam.Add("sign", sign);
            return newParam;
        }

        private static string Md5(string str)
        {
            var md5Hash = MD5.Create();
            var data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(str));
            var sBuilder = new StringBuilder();
            foreach (var t in data)
            {
                sBuilder.Append(t.ToString("x2"));
            }
            return sBuilder.ToString();
        }

        private string BuildParam(Dictionary<string, string> param)
        {
            param = Sign(param);
            if (param == null || param.Count == 0) return "";
            var sb = new StringBuilder();
            foreach (var key in param.Keys)
            {
                sb.AppendFormat("{0}={1}&", key, param[key]);
            }
            return sb.ToString().Substring(0, sb.Length - 1);
        }

        private string Post(string url, Dictionary<string, string> parameters)
        {
            var request = (HttpWebRequest)WebRequest.CreateDefault(new Uri(url));
            request.Method = "POST";
            request.UserAgent = "PayJsHelper";
            request.ContentType = "application/x-www-form-urlencoded";
            var str = BuildParam(parameters);
            if (str != "")
            {
                var data = Encoding.UTF8.GetBytes(str);
                using (var stream = request.GetRequestStream())
                {
                    stream.Write(data, 0, data.Length);
                }
            }
            using (var s = request.GetResponse().GetResponseStream())
            {
                var reader = new StreamReader(s ?? throw new InvalidOperationException(), Encoding.UTF8);
                return reader.ReadToEnd();
            }
        }
    }
}